
在上一篇,我們完成了 refresh token 以及 jwt 的實作
接下來我們要把一切都接起來,整合我們在系統裡常見的幾個實作
註冊,登入,登出,以及刷新 token (Reuse Detection)的最上層實作
並且我們把 API 接起來,然後完美的完成認證系統的小專案
我們統一在 ./src/routes 的資料夾內,如果有需要練習的讀者,
記得要把先前專案的東西都放進來,細節我就不加贅述了
[補充說明]:
可能有些人對程式方面有 clean architecture 方面有特別要求
因此會看不慣我把 sql 全部統合在一起,因為這裡主要是 demo 用途,所以
無論,註冊,登入等擺在一起是為了好給讀者可以一次閱讀到底
我目前把專案放在 github上了,基本上歡迎研究取用
怕把程式碼在這裡貼太多,淪為流水帳,想知詳情的部分,可以看我的 github 連結
我們這裡大概用流程圖以及決策的方式解釋
在這裡註冊有幾個要點,除了驗證以外,
我們要把使用者註冊的訊息存到 db 那我們這裡用 bun sqlite 演示
這是註冊的基本
這裡要思考的決策為
程式碼我提供在 github: 註冊
登入的重點不外乎就是要驗證帳密,有的話就拿到 jwt token 以及 refreshToken 就放行
程式碼我提供在 github: 登入
登出不外乎就是要清掉 refreshToken 並且把對應的 cookies Max age 設為 0
程式碼我提供在 github: 登出
這裏是執行 rotate token 的做法
程式碼我提供在 github: 輪替 token
我們這裡要建立 middleware 去驗證 token
我們建立 ./src/middleware/authenticate.ts
這段 code 比較幾單我直接提供,基本上就是擋住沒有提供token 或過期的路由
如果要找對應在 github : middleware
import { verifyAccessToken } from '../jwt';
export interface AuthenticateResult {
user_id: string
email: string
}
export const authenticate = async (req: Request): Promise<AuthenticateResult | Response> => {
const auth = req.headers.get('authorization');
if (auth === null)
return Response.json({
error: '需要授權'
}, { status: 401 });
if (!auth.startsWith('Bearer '))
return Response.json({
error: '格式錯誤 Authorization'
}, { status: 401 });
return Promise.try(() => verifyAccessToken(auth.slice(7)))
.then((payload) => ({ user_id: payload.sub, email: payload.email }))
.catch(() => Response.json({ error: 'token 無效或是過期' }, { status: 401 }));
}
最後我們把對應的都組裝起來
import { handleRegister } from './routes/register'
import { handleLogin } from './routes/login'
import { handleLogout } from './routes/logout'
import { handleRefresh } from './routes/refresh'
import { authenticate } from './middleware/authenticate'
Bun.serve({
port: 3000,
routes: {
"/api/auth/register": { POST: handleRegister },
"/api/auth/login": { POST: handleLogin },
"/api/auth/logout": { POST: handleLogout },
"/api/auth/refresh": { POST: handleRefresh },
"/api/sample": {
GET: async (req: Request): Promise<Response> => {
const result = await authenticate(req);
if (result instanceof Response)
return result;
const { user_id, email } = result;
return Response.json({ userId: user_id, email });
}
},
}
});
小提醒!! : 當然我們可以用前面的 Elysia 去實作,那我們這個只用 jose
其他都靠 bun 的威能就可以實現,如果想用 Elysia 去練習也是沒問題的!
到這裡,我們把 JWT + Session 的認證系統從底層機制到路由整合,
完整地串了一遍:註冊、登入、登出、以及帶有 Reuse Detection 的 refresh token 輪替,全部都掛上了實際可以動的 API。
如果你是跟著這系列一步步做下來的讀者,現在手上應該已經有一個「五臟俱全」、
可以直接拿去串接前端或是當作專案骨架的認證系統了。
不過這是比較簡單的版本,關於 Oauth, RBAC, ABAC ,以及 RateLimit 暴力破解等的實作可以弄得更複雜一些,
不過我們的主題基本上圍繞在 bun 上面,搞太複雜也沒意思,等有機會再深度展示這些